#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <random>
#include <chrono>
#include <atomic>

std::mutex cout_mutex;
std::atomic<bool> running(true);

struct Packet
{
    int seq_num;
    std::string data;
    bool is_ack;
};

std::queue<Packet> sender_queue;
std::queue<Packet> receiver_queue;
std::mutex sender_mutex;
std::mutex receiver_mutex;

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0, 1);

const double LOSS_PROBABILITY = 0.3;

void sender_thread(int total_packets)
{
    int next_seq = 0;
    int base = 0;
    const int timeout = 2000;

    while (base < total_packets)
    {
        if (next_seq < base + 1 && next_seq < total_packets)
        {
            Packet pkt;
            pkt.seq_num = next_seq;
            pkt.data = "Packet " + std::to_string(next_seq);
            pkt.is_ack = false;

            {
                std::lock_guard<std::mutex> lock(cout_mutex);
                std::cout << "Sender: Sending packet " << next_seq << std::endl;
            }

            if (dis(gen) >= LOSS_PROBABILITY)
            {
                std::lock_guard<std::mutex> lock(receiver_mutex);
                receiver_queue.push(pkt);
            }
            else
            {
                std::lock_guard<std::mutex> lock(cout_mutex);
                std::cout << "Sender: Packet " << next_seq << " lost" << std::endl;
            }

            next_seq++;
        }

        bool ack_received = false;
        {
            std::lock_guard<std::mutex> lock(sender_mutex);
            if (!sender_queue.empty())
            {
                Packet ack = sender_queue.front();
                sender_queue.pop();

                if (ack.seq_num >= base)
                {
                    base = ack.seq_num + 1;
                    ack_received = true;

                    std::lock_guard<std::mutex> lock(cout_mutex);
                    std::cout << "Sender: Received ACK " << ack.seq_num << std::endl;
                }
            }
        }

        if (!ack_received)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
            if (!ack_received && base < total_packets)
            {
                std::lock_guard<std::mutex> lock(cout_mutex);
                std::cout << "Sender: Timeout, resending packet " << base << std::endl;
                next_seq = base;
            }
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    running = false;
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Sender: Transmission complete" << std::endl;
}

void receiver_thread()
{
    int expected_seq = 0;

    while (running)
    {
        bool packet_received = false;
        {
            std::lock_guard<std::mutex> lock(receiver_mutex);
            if (!receiver_queue.empty())
            {
                Packet pkt = receiver_queue.front();
                receiver_queue.pop();
                packet_received = true;

                if (dis(gen) >= LOSS_PROBABILITY)
                {
                    if (pkt.seq_num == expected_seq)
                    {
                        std::lock_guard<std::mutex> lock(cout_mutex);
                        std::cout << "Receiver: Received packet " << pkt.seq_num << ", sending ACK" << std::endl;

                        Packet ack;
                        ack.seq_num = pkt.seq_num;
                        ack.is_ack = true;

                        {
                            std::lock_guard<std::mutex> lock(sender_mutex);
                            sender_queue.push(ack);
                        }

                        expected_seq++;
                    }
                    else
                    {
                        std::lock_guard<std::mutex> lock(cout_mutex);
                        std::cout << "Receiver: Out-of-order packet " << pkt.seq_num << ", expected " << expected_seq << ", discarding" << std::endl;
                    }
                }
                else
                {
                    std::lock_guard<std::mutex> lock(cout_mutex);
                    std::cout << "Receiver: ACK " << pkt.seq_num << " lost" << std::endl;
                }
            }
        }

        if (!packet_received)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    }

    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Receiver: Exiting" << std::endl;
}

int main()
{
    const int TOTAL_PACKETS = 10;

    std::thread sender(sender_thread, TOTAL_PACKETS);
    std::thread receiver(receiver_thread);

    sender.join();
    receiver.join();

    return 0;
}